package com.huabo.sql.record;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Jackie Liu on 2017/6/28.
 */
public class DomainModelAnalysis {

    private Class<?> clazz;
    //数据库名称
    private String tableName;
    //主键名称
    private String primaryKey;
    //默认的数据库字段拼接字符串，例如：_NAME, _NAME2, _NAME3
    private String defaultColumnArrayStr;
    //字段集合
    private List<TableColumn> tableColumnList = new ArrayList<TableColumn>();

    private static final Map<Class<?>, DomainModelAnalysis> map = new HashMap<>();

    /**
     * 获取实体类的信息
     */
    public void analysisBean() {
        Assert.notNull(clazz);
        map.put(clazz, this);

        //获取数据库表名称
        Table table = clazz.getAnnotation(Table.class);
        Assert.notNull(table, String.format("[%s] @Table is null", clazz.getName()));
        Assert.notNull(table.name(), String.format("[%s] the value of @Table's name  is null", clazz.getName()));

        tableName = table.name();

        ReflectionUtils.clearCache();
        //Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        Field[] fields = FieldUtils.getAllFields(clazz);
        for (Field field : fields) {
            if (field.getAnnotations().length <= 0) {
                continue;
            }
            TableColumn tableColumn = new TableColumn();
            tableColumn.setFieldName(field.getName());
            tableColumn.setJavaType(field.getType());

            String setMethodName = "set" + StringUtils.capitalize(field.getName());
            Method setMethod = ReflectionUtils.findMethod(clazz, setMethodName, field.getType());
            Assert.notNull(setMethod, String.format("[%s] : file name [%s] does not has set method. Expect method : %s", clazz.getName(), field.getName(), setMethodName));
            tableColumn.setFieldSetMethod(setMethod);

            String getMethodName = "get" + StringUtils.capitalize(field.getName());
            Method getMethod = ReflectionUtils.findMethod(clazz, getMethodName);
            Assert.notNull(getMethod, String.format("[%s] : file name [%s] does not has get method. Expect method : %s", clazz.getName(), field.getName(), getMethodName));
            tableColumn.setFieldGetMethod(getMethod);

            Id key = field.getAnnotation(Id.class);
            Column column = field.getAnnotation(Column.class);
            ColumnExtend columnExtend = field.getAnnotation(ColumnExtend.class);
            TransientField transientField = field.getAnnotation(TransientField.class);

            if (key != null) {
                Assert.notNull(column, String.format("[%s] : file name [%s] has @Id, but does not has @Column", clazz.getName(), field.getName()));
                primaryKey = column.name();
                tableColumn.setPrimaryKey(true);
            }
            if (column != null) {
                tableColumn.setColumnName(column.name().toUpperCase());
            }
            if (columnExtend != null) {
                tableColumn.setExtendType(columnExtend.extendType());
            }
            if (transientField != null) {
                tableColumn.setColumnName(transientField.name().toUpperCase());
                tableColumn.setTransient(true);
            }
            Assert.notNull(tableColumn.getColumnName(), String.format("[%s] : file name [%s] does not has column name (see @Column with name value or @TransientField with name value)", clazz.getName(), field.getName()));
            tableColumnList.add(tableColumn);
        }
        defaultColumnArrayStr = joinColumn(", ");
    }

    /**
     * 拼装数据库字段，split为分隔符
     *
     * @param split 分隔符
     * @return 如：_NAME1, _NAME2, _NAME3
     */
    public String joinColumn(String split) {
        StringBuilder stringBuilder = new StringBuilder();
        int index = 0;
        for (int i = 0, size = tableColumnList.size(); i < size; i++) {
            TableColumn tableColumn = tableColumnList.get(i);
            if (tableColumn.isTransient()) {
                continue;
            }
            if (index == 0) {
                stringBuilder.append(tableColumn.getColumnName());
                index++;
                continue;
            }
            stringBuilder.append(split).append(tableColumn.getColumnName());
        }
        return stringBuilder.length() > 0 ? stringBuilder.toString() : null;
    }

    /**
     * 拼装数据库字段，split为分隔符
     *
     * @param split 分隔符
     * @return 如：{"_NAME1, _NAME2, _NAME3", "?, ?, ?"}
     */
    public String[] joinColumnWithPlaceholder(String split) {
        StringBuilder stringBuilder = new StringBuilder();
        StringBuilder phBuilder = new StringBuilder();
        int index = 0;
        for (int i = 0, size = tableColumnList.size(); i < size; i++) {
            TableColumn tableColumn = tableColumnList.get(i);
            if (tableColumn.isTransient()) {
                continue;
            }
            if (index == 0) {
                stringBuilder.append(tableColumn.getColumnName());
                phBuilder.append(tableColumn.getPlaceholder());
                index++;
                continue;
            }
            stringBuilder.append(split).append(tableColumn.getColumnName());
            phBuilder.append(split).append(tableColumn.getPlaceholder());
        }
        return stringBuilder.length() > 0 ? new String[]{stringBuilder.toString(), phBuilder.toString()} : null;
    }

    public List<TableColumn> getTableColumnList() {
        return tableColumnList;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public String getDefaultColumnArrayStr() {
        return defaultColumnArrayStr;
    }

    public String getTableName() {
        return tableName;
    }

    public String getPrimaryKey() {
        return primaryKey;
    }

    /**
     * 获取tableColumn对象
     *
     * @param columnName 数据库列名
     * @return
     */
    public TableColumn getTableColumn(String columnName) {
        if (StringUtils.isEmpty(columnName)) {
            return null;
        }
        for (TableColumn tableColumn : tableColumnList) {
            if (tableColumn.getColumnName().equals(columnName)) {
                return tableColumn;
            }
        }
        return null;
    }

    /**
     * 获取tableColumn对象
     *
     * @param fieldName java属性名
     * @return
     */
    public TableColumn getTableColumn2(String fieldName) {
        if (StringUtils.isEmpty(fieldName)) {
            return null;
        }
        for (TableColumn tableColumn : tableColumnList) {
            if (tableColumn.getFieldName().equals(fieldName)) {
                return tableColumn;
            }
        }
        return null;
    }

    public static DomainModelAnalysis get(Class<?> clazz) {
        return map.get(clazz);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("DomainModelAnalysis{");
        sb.append("clazz=").append(clazz.getName());
        sb.append(", tableName='").append(tableName).append('\'');
        sb.append(", primaryKey='").append(primaryKey).append('\'');
        sb.append(", defaultColumnArrayStr='").append(defaultColumnArrayStr).append('\'');
        sb.append(", tableColumnList=").append(tableColumnList);
        sb.append('}');
        return sb.toString();
    }
}
